package rensoft.gwt.client.lang;

import com.google.gwt.event.shared.HandlerRegistration;

import java.util.List;

import static cs.java.lang.Lang.list;
import static cs.java.lang.Lang.unexpected;

import cs.java.lang.HasService;
import cs.java.lang.Service;




import rensoft.gwt.client.event.Event;
import rensoft.gwt.client.event.EventBus;
import rensoft.gwt.client.event.EventFunction;
import rensoft.gwt.client.event.HasEvents;

public class GwtBase implements HasEvents, HasService {

  private final EventBus eventBus = new EventBus();
  private HasService hasService;
  private final List<EventRegistration> registrations = list();

  public <Argument> HandlerRegistration addHandler(Event<Argument> event,
      EventFunction<Argument> function) {
    return eventBus.addHandler(event, function);
  }

  public Service getService() {
    return hasService.getService();
  }

  public void initialize(HasService value) {
    hasService = value;
  }

  protected <Parent, Argument> void fireEvent(Event<Argument> event, Argument argument) {
    eventBus.fireEvent(event, argument);
  }

  protected <Parent> void fireEvent(Event<Void> event) {
    fireEvent(event, null);
  }

  protected <T> T get(Class<T> service) {
    return getService().get(service);
  }

  protected <Argument> void register(HasEvents target, Event<Argument> event,
      EventFunction<Argument> function) {
    HandlerRegistration registration = target.addHandler(event, function);
    registrations.add(new EventRegistration(event, target, registration));
  }

  protected <Argument> void registerReplace(HasEvents target, Event<Argument> event,
      EventFunction<Argument> function) {
    List<EventRegistration> registrations = getRegistrationsForEvent(event);
    if (registrations.size() > 1) throw unexpected();
    if (registrations.size() == 1) {
      registrations.get(0).cancel();
      this.registrations.remove(registrations.get(0));
    }
    register(target, event, function);
  }

  protected <Argument> void unregister(Event<Argument> event) {
    List<EventRegistration> registrations = getRegistrationsForEvent(event);
    for (EventRegistration eventRegistration : registrations) {
      eventRegistration.cancel();
      this.registrations.remove(eventRegistration);
    }
  }

  private <Argument> List<EventRegistration> getRegistrationsForEvent(Event<Argument> event) {
    List<EventRegistration> registrations = list();
    for (EventRegistration eventRegistration : this.registrations)
      if (eventRegistration.getEvent() == event) registrations.add(eventRegistration);
    return registrations;
  }
}
